home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Game Programming Gurus
/
Tricks of the Windows Game Programming Gurus (SAMS)(2000).iso
/
Goodies
/
t3dlib3.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-30
|
23KB
|
887 lines
// T3DLIB3.CPP - Game Engine Part III, sound & music
// INCLUDES ///////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // include important windows stuff
#include <windowsx.h>
#include <mmsystem.h>
#include <objbase.h>
#include <iostream.h> // include important C/C++ stuff
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include <direct.h>
#include <wchar.h>
#include <ddraw.h> // directX includes
#include <dsound.h>
#include <dmksctrl.h>
#include <dmusici.h>
#include <dmusicc.h>
#include <dmusicf.h>
#include "T3DLIB3.H"
// DEFINES ////////////////////////////////////////////////
// TYPES //////////////////////////////////////////////////
// PROTOTYPES /////////////////////////////////////////////
// EXTERNALS /////////////////////////////////////////////
extern HWND main_window_handle; // access to main window handle in main module
// GLOBALS ////////////////////////////////////////////////
// directsound stuff
LPDIRECTSOUND lpds = NULL; // directsound interface pointer
DSBUFFERDESC dsbd; // directsound description
DSCAPS dscaps; // directsound caps
HRESULT dsresult; // general directsound result
DSBCAPS dsbcaps; // directsound buffer caps
LPDIRECTSOUNDBUFFER lpdsbprimary = NULL; // the primary mixing buffer
pcm_sound sound_fx[MAX_SOUNDS]; // the array of secondary sound buffers
WAVEFORMATEX pcmwf; // generic waveformat structure
// direct music globals
IDirectMusicPerformance *dm_perf = NULL; // the directmusic performance manager
IDirectMusicLoader *dm_loader = NULL; // the directmusic loader
// this hold all the directmusic midi objects
DMUSIC_MIDI dm_midi[DM_NUM_SEGMENTS];
int dm_active_id = -1; // currently active midi segment
// FUNCTIONS //////////////////////////////////////////////
int DSound_Load_WAV(char *filename, int control_flags)
{
// this function loads a .wav file, sets up the directsound
// buffer and loads the data into memory, the function returns
// the id number of the sound
HMMIO hwav; // handle to wave file
MMCKINFO parent, // parent chunk
child; // child chunk
WAVEFORMATEX wfmtx; // wave format structure
int sound_id = -1, // id of sound to be loaded
index; // looping variable
UCHAR *snd_buffer, // temporary sound buffer to hold voc data
*audio_ptr_1=NULL, // data ptr to first write buffer
*audio_ptr_2=NULL; // data ptr to second write buffer
DWORD audio_length_1=0, // length of first write buffer
audio_length_2=0; // length of second write buffer
// step one: are there any open id's ?
for (index=0; index < MAX_SOUNDS; index++)
{
// make sure this sound is unused
if (sound_fx[index].state==SOUND_NULL)
{
sound_id = index;
break;
} // end if
} // end for index
// did we get a free id?
if (sound_id==-1)
return(-1);
// set up chunk info structure
parent.ckid = (FOURCC)0;
parent.cksize = 0;
parent.fccType = (FOURCC)0;
parent.dwDataOffset = 0;
parent.dwFlags = 0;
// copy data
child = parent;
// open the WAV file
if ((hwav = mmioOpen(filename, NULL, MMIO_READ | MMIO_ALLOCBUF))==NULL)
return(-1);
// descend into the RIFF
parent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmioDescend(hwav, &parent, NULL, MMIO_FINDRIFF))
{
// close the file
mmioClose(hwav, 0);
// return error, no wave section
return(-1);
} // end if
// descend to the WAVEfmt
child.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hwav, &child, &parent, 0))
{
// close the file
mmioClose(hwav, 0);
// return error, no format section
return(-1);
} // end if
// now read the wave format information from file
if (mmioRead(hwav, (char *)&wfmtx, sizeof(wfmtx)) != sizeof(wfmtx))
{
// close file
mmioClose(hwav, 0);
// return error, no wave format data
return(-1);
} // end if
// make sure that the data format is PCM
if (wfmtx.wFormatTag != WAVE_FORMAT_PCM)
{
// close the file
mmioClose(hwav, 0);
// return error, not the right data format
return(-1);
} // end if
// now ascend up one level, so we can access data chunk
if (mmioAscend(hwav, &child, 0))
{
// close file
mmioClose(hwav, 0);
// return error, couldn't ascend
return(-1);
} // end if
// descend to the data chunk
child.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (mmioDescend(hwav, &child, &parent, MMIO_FINDCHUNK))
{
// close file
mmioClose(hwav, 0);
// return error, no data
return(-1);
} // end if
// finally!!!! now all we have to do is read the data in and
// set up the directsound buffer
// allocate the memory to load sound data
snd_buffer = (UCHAR *)malloc(child.cksize);
// read the wave data
mmioRead(hwav, (char *)snd_buffer, child.cksize);
// close the file
mmioClose(hwav, 0);
// set rate and size in data structure
sound_fx[sound_id].rate = wfmtx.nSamplesPerSec;
sound_fx[sound_id].size = child.cksize;
sound_fx[sound_id].state = SOUND_LOADED;
// set up the format data structure
memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
pcmwf.wFormatTag = WAVE_FORMAT_PCM; // pulse code modulation
pcmwf.nChannels = 1; // mono
pcmwf.nSamplesPerSec = 11025; // always this rate
pcmwf.nBlockAlign = 1;
pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
pcmwf.wBitsPerSample = 8;
pcmwf.cbSize = 0;
// prepare to create sounds buffer
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = control_flags | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = child.cksize;
dsbd.lpwfxFormat = &pcmwf;
// create the sound buffer
if (FAILED(lpds->CreateSoundBuffer(&dsbd,&sound_fx[sound_id].dsbuffer,NULL)))
{
// release memory
free(snd_buffer);
// return error
return(-1);
} // end if
// copy data into sound buffer
if (FAILED(sound_fx[sound_id].dsbuffer->Lock(0,
child.cksize,
(void **) &audio_ptr_1,
&audio_length_1,
(void **)&audio_ptr_2,
&audio_length_2,
DSBLOCK_FROMWRITECURSOR)))
return(0);
// copy first section of circular buffer
memcpy(audio_ptr_1, snd_buffer, audio_length_1);
// copy last section of circular buffer
memcpy(audio_ptr_2, (snd_buffer+audio_length_1),audio_length_2);
// unlock the buffer
if (FAILED(sound_fx[sound_id].dsbuffer->Unlock(audio_ptr_1,
audio_length_1,
audio_ptr_2,
audio_length_2)))
return(0);
// release the temp buffer
free(snd_buffer);
// return id
return(sound_id);
} // end DSound_Load_WAV
///////////////////////////////////////////////////////////
int DSound_Replicate_Sound(int source_id)
{
// this function replicates the sent sound and sends back the
// id of the replicated sound, you would use this function
// to make multiple copies of a gunshot or something that
// you want to play multiple times simulataneously, but you
// only want to load once
if (source_id!=-1)
{
// duplicate the sound buffer
// first hunt for an open id
for (int id=0; id < MAX_SOUNDS; id++)
{
// is this sound open?
if (sound_fx[id].state==SOUND_NULL)
{
// first make an identical copy
sound_fx[id] = sound_fx[source_id];
// now actually replicate the directsound buffer
if (FAILED(lpds->DuplicateSoundBuffer(sound_fx[source_id].dsbuffer,
&sound_fx[id].dsbuffer)))
{
// reset sound to NULL
sound_fx[id].dsbuffer = NULL;
sound_fx[id].state = SOUND_NULL;
// return error
return(-1);
} // end if
// now fix up id
sound_fx[id].id = id;
// return replicated sound
return(id);
} // end if found
} // end for id
} // end if
else
return(-1);
// else failure
return(-1);
} // end DSound_Replicate_Sound
//////////////////////////////////////////////////////////
int DSound_Init(void)
{
// this function initializes the sound system
static int first_time = 1; // used to track the first time the function
// is entered
// test for very first time
if (first_time)
{
// clear everything out
memset(sound_fx,0,sizeof(pcm_sound)*MAX_SOUNDS);
// reset first time
first_time = 0;
// create a directsound object
if (FAILED(DirectSoundCreate(NULL, &lpds, NULL)))
return(0);
// set cooperation level
if (FAILED(lpds->SetCooperativeLevel((HWND)main_window_handle,DSSCL_NORMAL)))
return(0);
} // end if
// initialize the sound fx array
for (int index=0; index<MAX_SOUNDS; index++)
{
// test if this sound has been loaded
if (sound_fx[index].dsbuffer)
{
// stop the sound
sound_fx[index].dsbuffer->Stop();
// release the buffer
sound_fx[index].dsbuffer->Release();
} // end if
// clear the record out
memset(&sound_fx[index],0,sizeof(pcm_sound));
// now set up the fields
sound_fx[index].state = SOUND_NULL;
sound_fx[index].id = index;
} // end for index
// return sucess
return(1);
} // end DSound_Init
///////////////////////////////////////////////////////////
int DSound_Shutdown(void)
{
// this function releases all the memory allocated and the directsound object
// itself
// first turn all sounds off
DSound_Stop_All_Sounds();
// now release all sound buffers
for (int index=0; index<MAX_SOUNDS; index++)
if (sound_fx[index].dsbuffer)
sound_fx[index].dsbuffer->Release();
// now release the directsound interface itself
if (lpds)
lpds->Release();
// return success
return(1);
} // end DSound_Shutdown
///////////////////////////////////////////////////////////
int DSound_Play(int id, int flags, int volume, int rate, int pan)
{
// this function plays a sound, the only parameter that
// works is the flags which can be 0 to play once or
// DSBPLAY_LOOPING
if (sound_fx[id].dsbuffer)
{
// reset position to start
if (FAILED(sound_fx[id].dsbuffer->SetCurrentPosition(0)))
return(0);
// play sound
if (FAILED(sound_fx[id].dsbuffer->Play(0,0,flags)))
return(0);
} // end if
// return success
return(1);
} // end DSound_Play
///////////////////////////////////////////////////////////
int DSound_Set_Volume(int id,int vol)
{
// this function sets the volume on a sound 0-100
if (sound_fx[id].dsbuffer->SetVolume(DSVOLUME_TO_DB(vol))!=DS_OK)
return(0);
// return success
return(1);
} // end DSound_Set_Volume
///////////////////////////////////////////////////////////
int DSound_Set_Freq(int id,int freq)
{
// this function sets the playback rate
if (sound_fx[id].dsbuffer->SetFrequency(freq)!=DS_OK)
return(0);
// return success
return(1);
} // end DSound_Set_Freq
///////////////////////////////////////////////////////////
int DSound_Set_Pan(int id,int pan)
{
// this function sets the pan, -10,000 to 10,0000
if (sound_fx[id].dsbuffer->SetPan(pan)!=DS_OK)
return(0);
// return success
return(1);
} // end DSound_Set_Pan
////////////////////////////////////////////////////////////
int DSound_Stop_Sound(int id)
{
// this function stops a sound from playing
if (sound_fx[id].dsbuffer)
{
sound_fx[id].dsbuffer->Stop();
sound_fx[id].dsbuffer->SetCurrentPosition(0);
} // end if
// return success
return(1);
} // end DSound_Stop_Sound
///////////////////////////////////////////////////////////
int DSound_Delete_All_Sounds(void)
{
// this function deletes all the sounds
for (int index=0; index < MAX_SOUNDS; index++)
DSound_Delete_Sound(index);
// return success always
return(1);
} // end DSound_Delete_All_Sounds
///////////////////////////////////////////////////////////
int DSound_Delete_Sound(int id)
{
// this function deletes a single sound and puts it back onto the available list
// first stop it
if (!DSound_Stop_Sound(id))
return(0);
// now delete it
if (sound_fx[id].dsbuffer)
{
// release the com object
sound_fx[id].dsbuffer->Release();
sound_fx[id].dsbuffer = NULL;
// return success
return(1);
} // end if
// return success
return(1);
} // end DSound_Delete_Sound
///////////////////////////////////////////////////////////
int DSound_Stop_All_Sounds(void)
{
// this function stops all sounds
for (int index=0; index<MAX_SOUNDS; index++)
DSound_Stop_Sound(index);
// return success
return(1);
} // end DSound_Stop_All_Sounds
///////////////////////////////////////////////////////////
int DSound_Status_Sound(int id)
{
// this function returns the status of a sound
if (sound_fx[id].dsbuffer)
{
ULONG status;
// get the status
sound_fx[id].dsbuffer->GetStatus(&status);
// return the status
return(status);
} // end if
else // total failure
return(-1);
} // end DSound_Status_Sound
///////////////////////////////////////////////////////////
int DMusic_Load_MIDI(char *filename)
{
// this function loads a midi segment
DMUS_OBJECTDESC ObjDesc;
HRESULT hr;
IDirectMusicSegment* pSegment = NULL;
int index; // loop var
// look for open slot for midi segment
int id = -1;
for (index = 0; index < DM_NUM_SEGMENTS; index++)
{
// is this one open
if (dm_midi[index].state == MIDI_NULL)
{
// validate id, but don't validate object until loaded
id = index;
break;
} // end if
} // end for index
// found good id?
if (id==-1)
return(-1);
// get current working directory
char szDir[_MAX_PATH];
WCHAR wszDir[_MAX_PATH];
if(_getcwd( szDir, _MAX_PATH ) == NULL)
{
return(-1);;
} // end if
MULTI_TO_WIDE(wszDir, szDir);
// tell the loader were to look for files
hr = dm_loader->SetSearchDirectory(GUID_DirectMusicAllTypes,wszDir, FALSE);
if (FAILED(hr))
{
return (-1);
} // end if
// convert filename to wide string
WCHAR wfilename[_MAX_PATH];
MULTI_TO_WIDE(wfilename, filename);
// setup object description
DD_INIT_STRUCT(ObjDesc);
ObjDesc.guidClass = CLSID_DirectMusicSegment;
wcscpy(ObjDesc.wszFileName, wfilename );
ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
// load the object and query it for the IDirectMusicSegment interface
// This is done in a single call to IDirectMusicLoader::GetObject
// note that loading the object also initializes the tracks and does
// everything else necessary to get the MIDI data ready for playback.
hr = dm_loader->GetObject(&ObjDesc,IID_IDirectMusicSegment, (void**) &pSegment);
if (FAILED(hr))
return(-1);
// ensure that the segment plays as a standard MIDI file
// you now need to set a parameter on the band track
// Use the IDirectMusicSegment::SetParam method and let
// DirectMusic find the trackby passing -1 (or 0xFFFFFFFF) in the dwGroupBits method parameter.
hr = pSegment->SetParam(GUID_StandardMIDIFile,-1, 0, 0, (void*)dm_perf);
if (FAILED(hr))
return(-1);
// This step is necessary because DirectMusic handles program changes and
// bank selects differently for standard MIDI files than it does for MIDI
// content authored specifically for DirectMusic.
// The GUID_StandardMIDIFile parameter must be set before the instruments are downloaded.
// The next step is to download the instruments.
// This is necessary even for playing a simple MIDI file
// because the default software synthesizer needs the DLS data
// for the General MIDI instrument set
// If you skip this step, the MIDI file will play silently.
// Again, you call SetParam on the segment, this time specifying the GUID_Download parameter:
hr = pSegment->SetParam(GUID_Download, -1, 0, 0, (void*)dm_perf);
if (FAILED(hr))
return(-1);
// at this point we have MIDI loaded and a valid object
dm_midi[id].dm_segment = pSegment;
dm_midi[id].dm_segstate = NULL;
dm_midi[id].state = MIDI_LOADED;
// return id
return(id);
} // end DMusic_Load_MIDI
//////////////////////////////////////////////////////////
int DMusic_Play(int id)
{
// play sound based on id
if (dm_midi[id].dm_segment && dm_midi[id].state!=MIDI_NULL)
{
// if there is an active midi then stop it
if (dm_active_id!=-1)
DMusic_Stop(dm_active_id);
// play segment and force tracking of state variable
dm_perf->PlaySegment(dm_midi[id].dm_segment, 0, 0, &dm_midi[id].dm_segstate);
dm_midi[id].state = MIDI_PLAYING;
// set the active midi segment
dm_active_id = id;
return(1);
} // end if
else
return(0);
} // end DMusic_Play
//////////////////////////////////////////////////////////
int DMusic_Stop(int id)
{
// stop a midi segment
if (dm_midi[id].dm_segment && dm_midi[id].state!=MIDI_NULL)
{
// play segment and force tracking of state variable
dm_perf->Stop(dm_midi[id].dm_segment, NULL, 0, 0);
dm_midi[id].state = MIDI_STOPPED;
// reset active id
dm_active_id = -1;
return(1);
} // end if
else
return(0);
} // end DMusic_Stop
///////////////////////////////////////////////////////////
int DMusic_Delete_MIDI(int id)
{
// this function deletes one MIDI segment
// Unload instruments this will cause silence.
// CloseDown unloads all instruments, so this call is also not
// strictly necessary.
if (dm_midi[id].dm_segment)
{
dm_midi[id].dm_segment->SetParam(GUID_Unload, -1, 0, 0, (void*)dm_perf);
// Release the segment and set to null
dm_midi[id].dm_segment->Release();
dm_midi[id].dm_segment = NULL;
dm_midi[id].dm_segstate = NULL;
dm_midi[id].state = MIDI_NULL;
} // end if
return(1);
} // end DMusic_Delete_MIDI
//////////////////////////////////////////////////////////
int DMusic_Delete_All_MIDI(void)
{
// delete all the MIDI
int index; // loop var
// free up all the segments
for (index = 0; index < DM_NUM_SEGMENTS; index++)
{
// Unload instruments this will cause silence.
// CloseDown unloads all instruments, so this call is also not
// strictly necessary.
if (dm_midi[index].dm_segment)
{
dm_midi[index].dm_segment->SetParam(GUID_Unload, -1, 0, 0, (void*)dm_perf);
// Release the segment and set to null
dm_midi[index].dm_segment->Release();
dm_midi[index].dm_segment = NULL;
dm_midi[index].dm_segstate = NULL;
dm_midi[index].state = MIDI_NULL;
} // end if
} // end for index
return(1);
} // end DMusic_Delete_All_MIDI
//////////////////////////////////////////////////////////
int DMusic_Status_MIDI(int id)
{
// this checks the status of a midi segment
if (dm_midi[id].dm_segment && dm_midi[id].state !=MIDI_NULL )
{
// get the status and translate to our defines
if (dm_perf->IsPlaying(dm_midi[id].dm_segment,NULL) == S_OK)
dm_midi[id].state = MIDI_PLAYING;
else
dm_midi[id].state = MIDI_STOPPED;
return(dm_midi[id].state);
} // end if
else
return(0);
} // end DMusic_Status_MIDI
///////////////////////////////////////////////////////////
int DMusic_Init(void)
{
// this function initializes directmusic, it also checks if directsound has
// been initialized, if so it connect the wave output to directsound, otherwise
// it creates it's own directsound object, hence you must start directsound up
// first if you want to use both directsound and directmusic
int index; // looping var
// set up directmusic
// initialize COM
if (FAILED(CoInitialize(NULL)))
{
// Terminate the application.
return(0);
} // end if
// create the performance
if (FAILED(CoCreateInstance(CLSID_DirectMusicPerformance,
NULL,
CLSCTX_INPROC,
IID_IDirectMusicPerformance,
(void**)&dm_perf)))
{
// return null
return(0);
} // end if
// initialize the performance, check if directsound is on-line if so, use the
// directsound object, otherwise create a new one
if (FAILED(dm_perf->Init(NULL, lpds, main_window_handle)))
{
return(0);// Failure -- performance not initialized
} // end if
// add the port to the performance
if (FAILED(dm_perf->AddPort(NULL)))
{
return(0);// Failure -- port not initialized
} // end if
// create the loader to load object(s) such as midi file
if (FAILED(CoCreateInstance(
CLSID_DirectMusicLoader,
NULL,
CLSCTX_INPROC,
IID_IDirectMusicLoader,
(void**)&dm_loader)))
{
// error
return(0);
} // end if
// reset all the midi segment objects
for (index = 0; index < DM_NUM_SEGMENTS; index++)
{
// reset the object
dm_midi[index].dm_segment = NULL;
dm_midi[index].dm_segstate = NULL;
dm_midi[index].state = MIDI_NULL;
dm_midi[index].id = index;
} // end for index
// reset the active id
dm_active_id = -1;
// all good baby
return(1);
} // end DMusic_Init
////////////////////////////////////////////////////////////
int DMusic_Shutdown(void)
{
int index;
// If there is any music playing, stop it. This is
// not really necessary, because the music will stop when
// the instruments are unloaded or the performance is
// closed down.
if (dm_perf)
dm_perf->Stop(NULL, NULL, 0, 0 );
// delete all the midis if they already haven't been
DMusic_Delete_All_MIDI();
// CloseDown and Release the performance object.
if (dm_perf)
{
dm_perf->CloseDown();
dm_perf->Release();
} // end if
// Release the loader object.
if (dm_loader)
dm_loader->Release();
// Release COM
CoUninitialize();
// return success
return(1);
} // end DMusic_Shutdown